/**************************************************************************************
*
*	Copyright (C) 2010 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	Texture.h
*
*	Description -	Wrapper class for DirectX 10 texture for now.
*
*	Comments	-	We should abstract this later in the future by deriving an interfice
*						from it.
*
*	Modification History:
*			Name			   Date					Description
*			CodeSushi	-	03/23/2010	-	Creation of this file
**************************************************************************************/

#ifndef _PSX_TEXTURE_H_
#define _PSX_TEXTURE_H_

#include "PulseSTD.h"
#include "ResourcePoolItem.h"
#include "ResourcePool.h"
#include "String.h"
#include "Singleton.h"
#include "SmartPointer.h"
#include "GraphicsTypes.h"

#include <d3d9.h>

namespace Pulse
{
	struct TextureDescription
	{
		ETextureFormat::Type	format;
		ETextureType::Type		type;
		ETextureUsage::Type		usage;
		UINT					width;
		UINT					height;
		UINT					depth;

		// can be "" if texture is loaded from memory.
		String					filename;
	
		TextureDescription( void )
			: format( ETextureFormat::A8R8G8B8 ),
			type( ETextureType::_2D ), usage( ETextureUsage::NORMAL ),
			width( 0 ), height( 0 ), depth( 0 ), filename( "" )
		{
		}
	};

	class Texture : public IResourcePoolItem
	{
	public:

		Texture( void );

		virtual ~Texture( void );

		// These defines the base interface for all derived resources
		virtual BOOL CreateResource( void );
		virtual BOOL DestroyResource( void );
		virtual BOOL DisableResource( void );
		virtual BOOL RestoreResource( void );

		// Default is NULL to use the resource name
		virtual BOOL LoadResource( const CHAR *pFilename = NULL );
		virtual BOOL SaveResource( const CHAR *pFilename = NULL );

		virtual void Initialize( void );

		virtual void Cleanup( void );

		// Creates an emtpy texture
		virtual EErrorCode::Type Create( UINT width = 512, UINT height = 512, 
			ETextureType::Type type = ETextureType::_2D, ETextureUsage::Type usage = ETextureUsage::NORMAL,
			ETextureFormat::Type format = ETextureFormat::A8R8G8B8, UINT depth = 1 );

		// Creates texture from file (implicitly loads from virtual system files).
		virtual EErrorCode::Type CreateFromFile( const CHAR *pFilename, ETextureType::Type type = ETextureType::_2D, 
			ETextureUsage::Type usage = ETextureUsage::NORMAL, ETextureFormat::Type format = ETextureFormat::DEFAULT );

		/********************************************************************************
		*	Function Name :	Bind
		*	Description	  :	Binds the texture to the renderer.
		*	In			  :	textureUnit - Index of the texture unit this texture will be bound to.
							bForceSet	- In case of render targets, by default, they are set to tender into them. However
											as you finish this process you will sometimes need to put this texture onto some object.
							clearFlags	- In case of render targets, it specifies whether it should be cleared or not. You can specify DEPTH, STENCIL, and TARGET flags.
		*	Out			  :	
		*	Return		  :	
		*	Example		  : 
		********************************************************************************/
		virtual void Bind( UINT textureUnit = 0, BOOL bForceSet = FALSE, DWORD clearFlags = EClearBufferType::RENDER_TARGET );

		virtual void Unbind( void );

		// Sets image data of the texture
		virtual void SetData( void *pData, UINT level = 0 ) const;
		virtual void SetDataFloat( void *pData, UINT size, UINT level = 0 ) const;

		// Returns image data of the texture.
		virtual void * GetData( UINT level = 0 ) const;

		// Returns texture information
		virtual TextureDescription * GetTextureDescription( void ) { return &m_textureDesc; }

		// Sets ratio for width and height
		void SetScaling( FLOAT x, FLOAT y );

		virtual UINT GetBitDepth( UINT level = 0 ) const { return m_textureDesc.depth; }

		// Clears render target
		virtual void ClearRendertarget( void );

		IDirect3DSurface9 *GetSurface( UINT level = 0 );

	private:

		// DX9 specific methods
		void NormalBind( void );					// How we would normally bind this texture...
		void BindRenderTarget( DWORD clearFlags );	// Bind behavior for textures as render target
		void NormalUnbind( void );					// How we would normally unbind this texture...
		void UnbindRenderTarget( void );			// Unbind behavior for textures as render target
		void GetUsagePoolMipMaps( DWORD &dxUsage, DWORD & dxPool, UINT &mips );
		D3DFORMAT GetFormat( ETextureFormat::Type &format, DWORD &usage );

		void CreateRenderTarget( IDirect3DDevice9 *pDevice );

		// Locks texture surface if possible.
		BOOL Lock( UINT level ) const;

	protected:

		struct RenderTargetDescription
		{
			D3DVIEWPORT9		viewport;			// Current viewport
			D3DVIEWPORT9		oldViewport;		// Previous viewport
			IDirect3DSurface9	*pZBuffer;			// Current Z-buffer
			IDirect3DSurface9	*pOldZBuffer;		// Previous Z-buffer
			BOOL				bViewportChange;	// Is viewport change necessary

			RenderTargetDescription( void )
				: pZBuffer( NULL ), pOldZBuffer( NULL ), bViewportChange( FALSE )
			{
			}

			~RenderTargetDescription( void )
			{
				PSX_SafeRelease( pZBuffer );
				PSX_SafeRelease( pOldZBuffer );
			}

		};

		typedef RenderTargetDescription RTDESC;

		union LockedArea
		{
			D3DLOCKED_RECT *pRect;
			D3DLOCKED_BOX *pBox;
		};

		// DirectX 9 texture
		union
		{
			IDirect3DTexture9			*m_p2DTexture;
			IDirect3DVolumeTexture9		*m_p3DTexture;
			IDirect3DCubeTexture9		*m_pCubeTexture;
		};

		TextureDescription	m_textureDesc;
		FLOAT				m_scaleWidth;		// Scaling factors
		FLOAT				m_scaleHeight;		// Scaling factors
		LockedArea			m_lockedArea;
		IDirect3DDevice9	*m_pDevice;
		RTDESC				*m_pRTDesc;			// Render target description
		IDirect3DSurface9	*m_pBackBuffer;		// Previous render target
		IDirect3DSurface9	*m_pSurface;		// Curent render target
		DWORD				m_usage;			// For what purpose the texture is going to be used
		UINT				m_textureUnit;
		BOOL				m_bIsForced;
		BOOL				m_bBoundAsTexture;

	};

	PSX_INLINE void Texture::SetScaling( FLOAT x, FLOAT y )
	{
		m_scaleWidth = x;
		m_scaleHeight = y;
	}

}

#endif /* _PSX_TEXTURE_H_ */